#include "logic.h"

using namespace std;
//local functions//
bool is_string_digit(string in)
{
	for (int i=0;i<in.size();i++)
	{
		if (!isdigit(in[i]))
			return false;
	}
	return true;
}

string int_to_str(int in)
{
	ostringstream outstr;
	if (in<10)
		outstr<<"0";
	outstr<<in;
	return outstr.str();
}

bool isLeapYear(int year)
{
	return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
} 


void calenderCheck(int &rday,int &rmon,int &ryear)
{
	int numdays;
	if (rmon==1 || rmon==3 || rmon==5 || rmon==7 || rmon==8 || rmon==10 || rmon==12)
	{
		numdays=31;
	}
	else if (rmon==4 || rmon==6 || rmon==11 || rmon==1)
	{
		numdays=30;
	}
	else
	{
		if (isLeapYear(ryear))
			numdays=29;
		else
			numdays=28;
	}

	if (rday>numdays)
	{
		rday=rday-numdays;
		rmon++;
	}
	if (rmon>13)
		ryear++;

	ryear=ryear%100;

}

int convert_day_to_int(string day)
{
	if (day=="sunday")
		return SUNDAY;
	else if (day=="monday")
		return MONDAY;
	else if (day=="tuesday")
		return TUESDAY;
	else if (day=="wednesday")
		return WEDNESDAY;
	else if (day=="thursday")
		return THURSDAY;
	else if (day=="friday")
		return FRIDAY;
	else if (day=="saturday")
		return SATURDAY;
	else
		return -1;

}
//end of local functions//

//private methods//
ProcessCommands::ProcessCommands()
{
	time ( &rawtime );
	timeinfo = localtime ( &rawtime );
}

void ProcessCommands:: splitString(string in)
{
	int startPosition=0,endPosition=0;
	string temp;

	//split up user input strings
	while (endPosition!=-1)
	{

		endPosition=in.find_first_of(' ');
		temp=in.substr(startPosition,endPosition);
		in=in.substr(endPosition+1);
		tokens.push_back(temp);

	}
}

COMMAND ProcessCommands::getCommandType()
{
	string in = tokens[0];
	if (in=="add")
		return ADD;
	else if (in=="update")
		return UPDATE;
	else if (in=="display")
		return DISPLAY;
	else if (in=="undo")
		return UNDO;
	else if (in=="delete")
		return DELETE;
	else 
		return ERROR;
}

bool ProcessCommands:: generate_update()
{
	int i=check_inverted_commas();
	if (i<1)
		return false;
	generate_event_term();

	//update key word -event,time,@
	int pos_key=i+1;//position of the keyword after the event term
	int term=pos_key+1;
	
	switch(get_update_type(pos_key))
	{
	case EVENT:
		{
			for (;term<tokens.size();term++)
				U.u_term+=tokens[term]+" ";
			U.u_term.resize(U.u_term.length()-1);
			break;
		}
	case TIME:
		{
			if (is_string_digit(tokens[term]))
			{
				if (tokens[term+1]=="to")
					U.u_stime=tokens[term];
				if (is_string_digit(tokens[term+2]))
					U.u_etime=tokens[term+2];
				else 
					return false;
			}
			else
				return false;
			break;
		}
	case PLACE:
		{
			for (;term<tokens.size();term++)
				U.u_term+=tokens[term]+" ";
			U.u_term.resize(U.u_term.length()-1);
			break;
		}
	case PRIORITY:
		{
			int count=0;
			for (;term<tokens.size() && tokens[term]=="*";term++)
				count++;
			switch (count)
			{
			case 0:
				return false;
				break;
			case 1:
				U.level=Schedule::LOW;
				break;
			case 2:
				U.level=Schedule::NORMAL;
				break;
			default:
				U.level=Schedule::HIGH;
				break;
			}
		}
	case DAY:
		if (is_string_digit(tokens[term]))
			U.u_term=tokens[term];
		else
			process_day_to_date(tokens[term]);
		break;
	default :
		return false;

	}

	return true;

}

bool ProcessCommands:: generate_add()
{
	string start_time,end_time;
	int i=0;
	int pos_key[5]={1};//position of key words
	/*
	pos_key[0] position of first inverted commas
	pos_key[1] position of second inverted commas
	pos_key[2] position of "on" keyword
	pos_key[3] position of "to" keyword
	pos_key[4] postion of "@" keyword
	*/
	bool keyword[5]={false};


	// user enter everything with spaces
	i=check_inverted_commas();//check for inverted commas

	if (i<1) //when user uses less than 1 inverted commas input format is wrong
		return false;
	pos_key[SECOND_INVERTED_COMMAS]=i; //second inverted commas
	pos_key[AT]=pos_key[TO]=pos_key[ON]=pos_key[SECOND_INVERTED_COMMAS]+1; //set all position of keyword to index of second inverted commas

	generate_event_term();
	S.set_name(event_term);

	//"on" is present, day/date will be check and set to the schedule object
	if (check_for_day(pos_key[ON]))
	{
		pos_key[TO]=pos_key[ON]+3;//set "to" keyword position
		keyword[ON]=true;
	}
	else
		pos_key[TO]=pos_key[SECOND_INVERTED_COMMAS]+2; //set "to" key word position if on is not present

	if (keyword[ON]==true) //if "on" key word is present check for time
	{
		if (check_for_time(pos_key[ON]+2,pos_key[TO]))
		{
			S.set_time(start_time,end_time);
			if (pos_key[TO]<tokens.size() && tokens[pos_key[TO]]=="to")
				pos_key[AT]=pos_key[TO]+2;
			else
				pos_key[AT]=pos_key[TO];
		}
	}
	else //if "on" key word not present check if time is present
	{
		if(check_for_time(pos_key[1]+1,pos_key[3]))
		{
			S.set_time(start_time,end_time);
			if (pos_key[TO]<tokens.size() && tokens[pos_key[TO]]=="to")
				pos_key[AT]=pos_key[TO]+2;
			else
				pos_key[AT]=pos_key[TO];
		}
	}

	//check if place is specified and set priority
	check_for_place(pos_key[AT]);

	//check if priority is specified and set priority
	check_for_priority(pos_key[AT]);

	return true;
}

bool ProcessCommands::generate_display()
{
	
	int i=0;

	i=check_inverted_commas();
	if (i==0)
		return false;
	else
	{
		generate_event_term();
		return true;
	}

}



void ProcessCommands::generate_event_term()
{
	int size=tokens.size();

	for (int i=2;i<size && tokens[i]!="\"";i++)
	{
		event_term+=tokens[i];
		event_term+=" ";
	}
	event_term.resize(event_term.length()-1);
}


UPDATE_TYPE ProcessCommands::get_update_type(int pos)
{

	if (tokens[pos]=="event")
	{
		U.update_type=EVENT;
		return EVENT;
	}
	else if (tokens[pos]=="time")
	{
		U.update_type=TIME;
		return TIME;
	}
	else if (tokens[pos]=="@")
	{
		U.update_type=PLACE;
		return PLACE;
	}
	else if (tokens[pos]=="priority")
	{
		U.update_type=PRIORITY;
		return PRIORITY;
	}
	else if (tokens[pos]=="day")
	{
		U.update_type=DAY;
		return DAY;
	}
	else 
		return NIL;
}

bool ProcessCommands::check_for_day(int pos)
{
	if (pos<tokens.size() && pos+1<tokens.size() && tokens[pos]=="on")
	{
		if (is_string_digit(tokens[pos+1]))
			S.set_day(tokens[pos+1]);
		else
			S.set_day(process_day_to_date(tokens[pos+1]));
		return true;

	}
	return false;
}

bool ProcessCommands::check_for_time(int pos_startT,int pos_to)
{
	string start_time;
	string end_time;
	bool starttime_present=false;
	if (pos_startT<tokens.size())
	{
		if (is_string_digit(tokens[pos_startT]))
		{
			start_time=tokens[pos_startT];
			starttime_present=true;
		}
	}
	if (starttime_present==true) //when there is start time need to set end time
	{
		if (pos_to<tokens.size() && tokens[pos_to]=="to")//end time is specified
			if (is_string_digit(tokens[pos_to+1]))
				end_time=tokens[pos_to+1];
			else
				end_time=create_1h_end_time(start_time);
		S.set_time(start_time,end_time);
		return true;
	}
	return false;
}

bool ProcessCommands::check_for_place(int pos)
{
	string place;
	if (pos+1<tokens.size()&& tokens[pos]=="@")
	{
		for (int i=pos+1;i<tokens.size() && tokens[i].find_first_of('*');i++) 
		{
			place+=tokens[i];
			place+=" ";
		}
		place.resize(place.length()-1);//remove the extra space;
		S.set_place(place);
		return true;
	}
	return false;

}

bool ProcessCommands::check_for_priority(int pos)
{

	int setpriority_fail=true;
	int priority_count=0;

	int i=pos;
	for (;i<tokens.size();i++)
	{
		if (tokens[i][0]=='*')
		{
			setpriority_fail=false;
			break;
		}
	}


	if (setpriority_fail==false)
	{
		for (int u=0;i<tokens[i].length();i++)
		{
			if (tokens[i][u]!='*')
			{
				break;
			}
			else
				priority_count++;
		}
	}

	switch (priority_count)
	{
	case 1:
		S.set_priority(Schedule::LOW);
		break;
	case 2:
		S.set_priority(Schedule::NORMAL);
		break;
	case 3:
		S.set_priority(Schedule::HIGH);
		break;
	default:
		S.set_priority(Schedule::NORMAL);
		break;
	}
	return true;

}

//create_1h_end_time
string ProcessCommands::create_1h_end_time (string startTime)
{
	char *c;
	c = new char [startTime.size()+1];
	string endTime;
	strcpy(c,startTime.c_str());
	c[1]=c[1]+1;//add 1 to the 2nd digit of the time
	if (c[1]>57) //greater than ascii value of 9 set ascii to 0
	{
		c[0]++; //increase the first digit by 1
		c[1]=48; //set the second digit to 0
	}

	endTime=c;
	delete []c;
	return endTime;
}

string ProcessCommands::process_day_to_date(string day)
{
	string date;
	day=to_lower_case(day);
	//current system clock attributes
	int current_Wday=timeinfo->tm_wday;
	int current_Mday=timeinfo->tm_mday;
	int current_Mon=timeinfo->tm_mon+1;
	int current_Year=timeinfo->tm_year+1900;

	//day specified by string
	int str_day=convert_day_to_int(day);

	//dates to be returned;
	int rtr_Mday=0;
	int rtr_Mon=current_Mon;
	int rtr_Year=current_Year;

	//check the current Week day compared to string's date
	if (current_Wday>str_day)
		rtr_Mday=current_Mday+(current_Wday-str_day);
	else
		rtr_Mday=current_Mday+(str_day+current_Wday);

	//perform calendercheck
	calenderCheck(rtr_Mday,rtr_Mon,rtr_Year);
	date=int_to_str(rtr_Year)+int_to_str(rtr_Mon)+int_to_str(rtr_Mday);
	return date;
}

Schedule ProcessCommands::get_add_schedule()
{
	return S;
}

int ProcessCommands::check_inverted_commas()
{
	if (tokens[1]!="\"")
		return 0;
	for (int i=2;i<tokens.size();i++)
	{
		if (tokens[i]=="\"")
		{
			return i;
		}
	}
	return 0;
}

bool ProcessCommands:: generate_del()
{
	int i=0;

	i=check_inverted_commas();
	if (i==0)
		return false;
	else
	{
		generate_event_term();
		return true;
	}
}

string ProcessCommands::get_event_term()
{
	return event_term;
}

vector<string> ProcessCommands::get_tokens()
{
	return tokens;
}

UPDATE_BLOCK ProcessCommands:: get_update_block()
{
	return U;
}



//clearing of containers///////////////////////////////////////////////////////////////
void ProcessCommands::clear_tokens()
{
	tokens.clear();
}

void ProcessCommands::clear_event_term()
{
	event_term.clear();
}


void ProcessCommands::clear_schedule()
{
	S.set_day("");
	S.set_name("");
	S.set_place("");
	S.set_priority(Schedule::NORMAL);
	S.set_time("","");
}

void ProcessCommands::clear_containers()
{
	clear_event_term();
	clear_tokens();
	clear_schedule();
}
/////////////////////////////////////////////////////////////////////